package progetto6.jade;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.wrapper.AgentController;
import jade.wrapper.ContainerController;

public class Manager extends Agent {
	
	@SuppressWarnings("serial")
	private class End extends OneShotBehaviour{

		@Override
		public void action() {
			System.out.println("Manager: Inizio terminazione dei player");
			ACLMessage message = new ACLMessage(ACLMessage.CANCEL);
			for (int i = 0; i < players.length; i++) {
				message.addReceiver(players[i]);
			}
			myAgent.send(message);
			log.save();
			myAgent.doDelete();
			System.out.println("Manager: Fine programma");
		}		
	}

	@SuppressWarnings("serial")
	private class Game extends Behaviour{
		
		private int game = 0;
		private int step = 0;
		private Integer lastPlayer;
		private Choice lastChoice;
		private boolean end = false;

		@SuppressWarnings("unchecked")
		@Override
		public void action() {
			MessageTemplate tpl = MessageTemplate.MatchPerformative(ACLMessage.INFORM);
			ACLMessage message = myAgent.receive(tpl);
			List<Object> content;
			Integer pid;
			Choice c;
			if (message != null) {
				try {
					switch (step) {
					case 0:
//						System.out.println("Manager:step=" + step);
						content = (List<Object>) message.getContentObject();
						lastPlayer = (Integer) content.get(0);
						lastChoice = (Choice) content.get(1);
						log.toLog(game, lastPlayer.intValue()-1, lastChoice);
						step++;
//						System.out.println("Manager:Game #" + game+ " giocato dal Player #" + lastPlayer.intValue());
						break;
					case 1:
						int scoreP1 = 0;
						int scoreP2 = 0;
//						System.out.println("Manager:step=" + step);
						content = (List<Object>) message
								.getContentObject();
						pid = (Integer) content.get(0);
						c = (Choice) content.get(1);
						log.toLog(game, pid-1, c);
//						System.out.println("Manager:Game #" + game+ " giocato dal Player #" + pid.intValue());
						ACLMessage reply1 = new ACLMessage(ACLMessage.INFORM);//for player 1
						ACLMessage reply2 = new ACLMessage(ACLMessage.INFORM);//for player 2
						reply1.addReceiver(players[0]);
						reply2.addReceiver(players[1]);
						if(lastPlayer.intValue()-1 == 0){//primo player
							scoreP1 = playoff[lastChoice.ordinal()][c.ordinal()][0];
							scoreP2 = playoff[lastChoice.ordinal()][c.ordinal()][1];
						}else{//secondo player
							scoreP1 = playoff[c.ordinal()][lastChoice.ordinal()][0];
							scoreP2 = playoff[c.ordinal()][lastChoice.ordinal()][1];
						}					
						reply1.setContentObject(new Integer(scoreP1));
						reply2.setContentObject(new Integer(scoreP2));
						step = 0;
						if(game == nGame-1){
							reply1.setConversationId("EG");
							reply2.setConversationId("EG");
							myAgent.addBehaviour(new Registration());
							end = true;
							System.out.println("Fine gioco");
						}
						myAgent.send(reply1);
						myAgent.send(reply2);
						game++;
						break;
					}
				} catch (UnreadableException e) {
					System.out.println("COD3");
					e.printStackTrace();
				} catch (IOException e) {
					System.out.println("COD4");
					e.printStackTrace();
				}
			} else {
//				System.out.println("Manager:Game block");
				block();
			}
		}

		@Override
		public boolean done() {
			return end;
		}	
	}
	
	private class Registration extends Behaviour{

		private static final long serialVersionUID = 2992905515672613900L;
		private int nPlayer = 0;
		private int firstPlayer = -1;
		private Strategy firstStrategy;

		@SuppressWarnings("unchecked")
		@Override
		public void action() {
			List<Object> content;
			MessageTemplate tpl = MessageTemplate.MatchPerformative(ACLMessage.SUBSCRIBE);
			ACLMessage message = myAgent.receive(tpl);
			if (message != null) {
				try {
					switch (nPlayer) {
					case 0:
						nPlayer++;
						if (message.getContentObject() != null) {
							content = (List<Object>) message.getContentObject();
							firstPlayer = ((Integer) content.get(0)).intValue();
							firstStrategy = (Strategy) content.get(1);
						}
						break;
					case 1:
						nPlayer++;
						if (firstPlayer != -1) {
							content = (List<Object>) message.getContentObject();
							if (firstPlayer == 1) {
								log.registry(firstStrategy);
								log.registry((Strategy) content.get(1));
							} else {
								log.registry((Strategy) content.get(1));
								log.registry(firstStrategy);
							}
						}
						if (!pool.isEmpty()) {
							for (int i = 0; i < players.length; i++) {
								ACLMessage reply = new ACLMessage(ACLMessage.SUBSCRIBE);
								reply.addReceiver(players[i]);
								reply.setContentObject(pool.poll());
								myAgent.send(reply);
								System.out.println("Manager:Player #"+ (i + 1) + " registrato con la rispettiva strategia");
							}
							System.out.println("Inizio gioco");
							myAgent.addBehaviour(new Game());
						} else {
							myAgent.addBehaviour(new End());
						}
						break;
					}
				} catch (IOException | UnreadableException e) {
					System.out.println("COD1");
					e.printStackTrace();
				}
			} else {
				// System.out.println("Manager:Registration block");
				block();
			}
		}

		@Override
		public boolean done() {
			return (pool.isEmpty() && nPlayer == N_PLAYERS) || nPlayer == N_PLAYERS;
		}
	}

	private static final long serialVersionUID = -5371727539484788706L;
	private static final int N_PLAYERS = 2;
	
	private int nGame;
	private final int[][][] playoff = {{{3,3},{0,5}},{{5,0},{2,2}}};
	private AID[] players = new AID[N_PLAYERS];
	private Queue<Strategy> pool = new LinkedList<Strategy>();
	private Logger log;
	
	@Override
	protected void setup() {
		System.out.println("Manager pronto ad avviare i Player");
		Object[] args = getArguments();
		if(args.length != 2){
			System.out.println("argomenti incorretti");
			doDelete();
		}
		nGame = Integer.parseInt((String) args[0]);
		if(nGame <= 0){
			System.out.println("Numero di giochi non corretto poiche' minore o uguale a zero");
			doDelete();
		}
		inizializePool();
		log = new Logger((String) args[1], nGame, pool.size());
		for (int i = 0; i < N_PLAYERS; i++) {
			ContainerController cc =null;
			AgentController ac =null;
			try {
				cc = getContainerController();
				String aid = "p"+(i+1);
				ac = cc.createNewAgent(aid, "progetto6.jade.Player", new Object[] {new Integer(i+1), getAID()}); //gli passo semplicemente l'id e l'AID del manager
				ac.start();
				players[i] = new AID(aid, AID.ISLOCALNAME);
			} catch (Exception e) {
				System.out.println("COD2");
				System.out.println("Errore nella creazione degli agenti");
				doDelete();
			}
		}
		System.out.println("Il manager ha avviato i player");
		addBehaviour(new Registration());
	}	

	private void inizializePool() {
		pool.add(new DStrategy());
		pool.add(new FixedRandomStrategy(nGame, 20));
		pool.add(new RandomStrategy());
		pool.add(new MinorityScoreStrategy(nGame, 20));
		pool.add(new MassiveRetaliatoryStrikeStrategy());
		pool.add(new FixedStrategy(nGame, 20));
		pool.add(new TesterStrategy(2, 2));
		pool.add(new FixedRandomStrategy(nGame, 30));
		pool.add(new MassiveRetaliatoryStrikeStrategy());
		pool.add(new MinorityScoreStrategy(nGame, 30));
		pool.add(new TesterStrategy(2, 2));
		pool.add(new FixedStrategy(nGame, 30));
		pool.add(new MassiveRetaliatoryStrikeStrategy());
		pool.add(new FixedRandomStrategy(nGame, 50));
		pool.add(new DStrategy());
		pool.add(new MinorityScoreStrategy(nGame, 50));
		pool.add(new RandomStrategy());
		pool.add(new FixedStrategy(nGame, 50));
	}
}
